/**
 * @file trading_main.cpp
 * @brief 交易系统的主程序入口
 * 
 * @details 该文件实现了交易系统的主程序入口，负责初始化和协调交易系统的各个组件，
 * 包括交易引擎、订单网关和市场数据消费者。程序根据命令行参数配置交易策略类型和相关参数，
 * 然后启动各个组件并管理它们的生命周期。如果选择了随机交易算法，还会生成随机订单进行测试。
 * 
 * @author 原作者
 * @date 2023
 */

#include <csignal>

#include "strategy/trade_engine.h"
#include "order_gw/order_gateway.h"
#include "market_data/market_data_consumer.h"

#include "common/logging.h"

/**
 * @brief 交易系统的主要组件
 * 
 * @details 这些全局变量保存了交易系统的主要组件实例，包括日志记录器、交易引擎、
 * 市场数据消费者和订单网关。这些组件在主函数中初始化并在程序结束时释放。
 */

/**
 * @brief 日志记录器
 * 
 * @details 用于记录交易系统的日志信息，包括组件初始化、订单发送和系统状态等。
 */
Common::Logger *logger = nullptr;

/**
 * @brief 交易引擎
 * 
 * @details 交易系统的核心组件，负责处理市场数据、管理订单和执行交易策略。
 */
Trading::TradeEngine *trade_engine = nullptr;

/**
 * @brief 市场数据消费者
 * 
 * @details 负责接收和处理来自交易所的市场数据，包括快照和增量更新。
 */
Trading::MarketDataConsumer *market_data_consumer = nullptr;

/**
 * @brief 订单网关
 * 
 * @details 负责将订单请求发送到交易所，并接收订单响应。
 */
Trading::OrderGateway *order_gateway = nullptr;

/**
 * @brief 交易系统的主函数
 * 
 * @details 该函数是交易系统的入口点，负责解析命令行参数、初始化各个组件、
 * 启动交易引擎、订单网关和市场数据消费者，并管理它们的生命周期。
 * 如果选择了随机交易算法，还会生成随机订单进行测试。
 * 程序会等待直到没有活动（至少60秒没有事件），然后安全地停止并清理资源。
 * 
 * @param argc 命令行参数数量
 * @param argv 命令行参数数组，包括：
 * - argv[1]: CLIENT_ID - 客户端标识符
 * - argv[2]: ALGO_TYPE - 算法类型（MAKER/TAKER/RANDOM）
 * - 后续参数以组为单位，每组包含5个参数，对应一个交易工具的配置：
 *   - CLIP_N: 价格剪裁参数，用于限制订单价格偏离市场价格的范围
 *   - THRESH_N: 阈值参数，用于决定交易策略的触发条件
 *   - MAX_ORDER_SIZE_N: 最大订单大小，用于风险控制
 *   - MAX_POS_N: 最大仓位大小，用于风险控制
 *   - MAX_LOSS_N: 最大允许损失，用于风险控制
 * 
 * @return 程序的退出状态码，成功时为0
 */
int main(int argc, char **argv) {
  // 检查命令行参数数量是否足够
  if(argc < 3) {
    FATAL("USAGE trading_main CLIENT_ID ALGO_TYPE [CLIP_1 THRESH_1 MAX_ORDER_SIZE_1 MAX_POS_1 MAX_LOSS_1] [CLIP_2 THRESH_2 MAX_ORDER_SIZE_2 MAX_POS_2 MAX_LOSS_2] ...");
  }

  /**
   * @brief 解析客户端标识符并初始化随机数生成器
   * 
   * @details 从命令行参数中获取客户端标识符，并使用它作为随机数生成器的种子，
   * 以确保不同客户端生成不同的随机序列。
   */
  const Common::ClientId client_id = atoi(argv[1]);
  srand(client_id); // 使用客户端ID作为随机数生成器的种子

  /**
   * @brief 解析算法类型
   * 
   * @details 从命令行参数中获取算法类型字符串，并将其转换为相应的枚举值。
   * 算法类型决定了交易引擎将使用的交易策略（做市商、流动性接受者或随机交易）。
   */
  const auto algo_type = stringToAlgoType(argv[2]);

  /**
   * @brief 创建日志记录器
   * 
   * @details 创建一个日志记录器实例，用于记录交易系统的日志信息。
   * 日志文件名包含客户端标识符，以区分不同客户端的日志。
   */
  logger = new Common::Logger("trading_main_" + std::to_string(client_id) + ".log");

  /**
   * @brief 设置随机交易算法的睡眠时间
   * 
   * @details 定义随机交易算法在发送订单之间的睡眠时间，单位为微秒。
   * 这个参数用于控制订单发送的频率，防止发送过快。
   */
  const int sleep_time = 20 * 1000; // 20毫秒，单位为微秒

  /**
   * @brief 初始化无锁队列用于组件间通信
   * 
   * @details 创建三个无锁队列用于交易系统组件之间的通信：
   * 1. client_requests: 用于交易引擎发送订单请求到订单网关
   * 2. client_responses: 用于订单网关将订单响应发送到交易引擎
   * 3. market_updates: 用于市场数据消费者将市场数据更新发送到交易引擎
   * 
   * 这些无锁队列允许组件在不同线程中安全高效地通信，无需使用互斥锁。
   */
  Exchange::ClientRequestLFQueue client_requests(ME_MAX_CLIENT_UPDATES);
  Exchange::ClientResponseLFQueue client_responses(ME_MAX_CLIENT_UPDATES);
  Exchange::MEMarketUpdateLFQueue market_updates(ME_MAX_MARKET_UPDATES);

  /**
   * @brief 时间字符串缓存
   * 
   * @details 用于在日志记录中存储当前时间的字符串表示，避免重复创建。
   */
  std::string time_str;

  /**
   * @brief 交易引擎配置映射
   * 
   * @details 存储每个交易工具的配置信息，包括价格剪裁参数、阈值参数和风险控制参数。
   * 这些配置将用于初始化交易引擎和交易策略。
   */
  TradeEngineCfgHashMap ticker_cfg;

  /**
   * @brief 从命令行参数解析并初始化交易引擎配置
   * 
   * @details 从命令行参数中解析每个交易工具的配置信息，并存储到ticker_cfg映射中。
   * 每个交易工具的配置包括以下参数：
   * 1. 价格剪裁参数（CLIP）：用于限制订单价格偏离市场价格的范围
   * 2. 阈值参数（THRESH）：用于决定交易策略的触发条件
   * 3. 风险控制参数：
   *    - 最大订单大小（MAX_ORDER_SIZE）
   *    - 最大仓位大小（MAX_POS）
   *    - 最大允许损失（MAX_LOSS）
   */
  size_t next_ticker_id = 0;
  for (int i = 3; i < argc; i += 5, ++next_ticker_id) {
    ticker_cfg.at(next_ticker_id) = {static_cast<Qty>(std::atoi(argv[i])), std::atof(argv[i + 1]),
                                     {static_cast<Qty>(std::atoi(argv[i + 2])),
                                      static_cast<Qty>(std::atoi(argv[i + 3])),
                                      std::atof(argv[i + 4])}};
  }

  /**
   * @brief 初始化并启动交易引擎
   * 
   * @details 创建交易引擎实例，并传入客户端标识符、算法类型、交易工具配置和通信队列。
   * 交易引擎是交易系统的核心组件，负责处理市场数据、管理订单和执行交易策略。
   */
  logger->log("%:% %() % Starting Trade Engine...\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str));
  trade_engine = new Trading::TradeEngine(client_id, algo_type,
                                          ticker_cfg,
                                          &client_requests,
                                          &client_responses,
                                          &market_updates);
  trade_engine->start(); // 启动交易引擎线程

  /**
   * @brief 订单网关配置
   * 
   * @details 定义订单网关的网络配置，包括IP地址、网络接口和端口。
   * 这里使用本地回环地址和接口进行测试。
   */
  const std::string order_gw_ip = "127.0.0.1"; // 本地回环地址
  const std::string order_gw_iface = "lo";    // 本地回环网络接口
  const int order_gw_port = 12345;           // 订单网关端口

  /**
   * @brief 初始化并启动订单网关
   * 
   * @details 创建订单网关实例，并传入客户端标识符、通信队列和网络配置。
   * 订单网关负责将订单请求发送到交易所，并接收订单响应。
   */
  logger->log("%:% %() % Starting Order Gateway...\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str));
  order_gateway = new Trading::OrderGateway(client_id, &client_requests, &client_responses, order_gw_ip, order_gw_iface, order_gw_port);
  order_gateway->start(); // 启动订单网关线程

  /**
   * @brief 市场数据消费者配置
   * 
   * @details 定义市场数据消费者的网络配置，包括网络接口、快照和增量更新的IP地址和端口。
   * 快照和增量更新使用不同的多播地址和端口。
   */
  const std::string mkt_data_iface = "lo";       // 本地回环网络接口
  const std::string snapshot_ip = "233.252.14.1"; // 快照数据的多播地址
  const int snapshot_port = 20000;               // 快照数据的端口
  const std::string incremental_ip = "233.252.14.3"; // 增量更新的多播地址
  const int incremental_port = 20001;               // 增量更新的端口

  /**
   * @brief 初始化并启动市场数据消费者
   * 
   * @details 创建市场数据消费者实例，并传入客户端标识符、市场数据更新队列和网络配置。
   * 市场数据消费者负责接收和处理来自交易所的市场数据，包括快照和增量更新。
   */
  logger->log("%:% %() % Starting Market Data Consumer...\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str));
  market_data_consumer = new Trading::MarketDataConsumer(client_id, &market_updates, mkt_data_iface, snapshot_ip, snapshot_port, incremental_ip, incremental_port);
  market_data_consumer->start(); // 启动市场数据消费者线程

  /**
   * @brief 等待组件初始化
   * 
   * @details 等往10秒，确保所有组件都已经完全初始化并准备就绪。
   */
  usleep(10 * 1000 * 1000); // 等往10秒

  /**
   * @brief 初始化交易引擎的最后事件时间
   * 
   * @details 设置交易引擎的最后事件时间为当前时间，用于跟踪交易引擎的活动状态。
   */
  trade_engine->initLastEventTime();

  /**
   * @brief 随机交易算法实现
   * 
   * @details 当选择随机交易算法时，直接在主函数中实现算法逻辑，而不是创建一个新的交易算法类。
   * 该算法生成具有随机属性的订单，并随机取消一些订单，用于测试交易系统的功能。
   */
  if (algo_type == AlgoType::RANDOM) {
    /**
     * @brief 初始化随机交易算法的参数
     * 
     * @details 初始化订单ID、已发送订单的容器和每个交易工具的基准价格。
     * 订单ID从客户端ID的1000倍开始，以确保不同客户端的订单ID不会重复。
     * 每个交易工具的基准价格在100到199之间随机生成。
     */
    Common::OrderId order_id = client_id * 1000; // 初始订单ID，从客户端ID的1000倍开始
    std::vector<Exchange::MEClientRequest> client_requests_vec; // 存储已发送的订单，用于后续取消
    std::array<Price, ME_MAX_TICKERS> ticker_base_price; // 每个交易工具的基准价格
    
    // 初始化每个交易工具的基准价格，范围为100-199
    for (size_t i = 0; i < ME_MAX_TICKERS; ++i)
      ticker_base_price[i] = (rand() % 100) + 100;
    
    /**
     * @brief 主交易循环
     * 
     * @details 循环生成最多10000个随机订单，并随机取消一些订单。
     * 如果交易引擎在60秒内没有活动，则提前结束循环。
     */
    for (size_t i = 0; i < 10000; ++i) {
      /**
       * @brief 生成随机订单参数
       * 
       * @details 随机选择交易工具、价格、数量和侧向（买入或卖出）。
       * 价格基于交易工具的基准价格加上一个随机偏移（1-10）。
       * 数量在2-101之间随机生成。
       * 侧向有平等的概率是买入或卖出。
       */
      const Common::TickerId ticker_id = rand() % Common::ME_MAX_TICKERS; // 随机选择交易工具
      const Price price = ticker_base_price[ticker_id] + (rand() % 10) + 1; // 基准价格加上随机偏移
      const Qty qty = 1 + (rand() % 100) + 1; // 随机数量（2-101）
      const Side side = (rand() % 2 ? Common::Side::BUY : Common::Side::SELL); // 随机侧向

      // 创建新订单请求并发送
      Exchange::MEClientRequest new_request{Exchange::ClientRequestType::NEW, client_id, ticker_id, order_id++, side,
                                            price, qty};
      trade_engine->sendClientRequest(&new_request);
      usleep(sleep_time); // 等待一段时间，控制发送频率

      // 将新订单添加到容器中，并随机选择一个订单取消
      client_requests_vec.push_back(new_request);
      const auto cxl_index = rand() % client_requests_vec.size(); // 随机选择要取消的订单
      auto cxl_request = client_requests_vec[cxl_index];
      cxl_request.type_ = Exchange::ClientRequestType::CANCEL; // 将请求类型改为取消
      trade_engine->sendClientRequest(&cxl_request);
      usleep(sleep_time); // 等待一段时间，控制发送频率

      /**
       * @brief 检查交易引擎的活动状态
       * 
       * @details 如果交易引擎在至少60秒内没有事件，则提前结束循环。
       * 这可能表示交易所不再响应或系统处于空闲状态。
       */
      if (trade_engine->silentSeconds() >= 60) {
        logger->log("%:% %() % Stopping early because been silent for % seconds...\n", __FILE__, __LINE__, __FUNCTION__,
                    Common::getCurrentTimeStr(&time_str), trade_engine->silentSeconds());

        break; // 提前结束循环
      }
    }
  }

  /**
   * @brief 等待交易引擎处于静默状态
   * 
   * @details 在关闭系统之前，等待交易引擎至少60秒没有活动。
   * 这确保所有的交易活动都已经完成，系统处于稳定状态，
   * 可以安全地关闭而不会丢失数据或导致不一致的状态。
   */
  while (trade_engine->silentSeconds() < 60) {
    logger->log("%:% %() % Waiting till no activity, been silent for % seconds...\n", __FILE__, __LINE__, __FUNCTION__,
                Common::getCurrentTimeStr(&time_str), trade_engine->silentSeconds());

    using namespace std::literals::chrono_literals;
    std::this_thread::sleep_for(30s);
  }

  trade_engine->stop();
  market_data_consumer->stop();
  order_gateway->stop();

  using namespace std::literals::chrono_literals;
  std::this_thread::sleep_for(10s);

  delete logger;
  logger = nullptr;
  delete trade_engine;
  trade_engine = nullptr;
  delete market_data_consumer;
  market_data_consumer = nullptr;
  delete order_gateway;
  order_gateway = nullptr;

  std::this_thread::sleep_for(10s);

  exit(EXIT_SUCCESS);
}
